package net.ermannofranco.xml.schema;

import java.util.ArrayList;

import net.ermannofranco.xml.BlankLine;
import net.ermannofranco.xml.ITag;
import net.ermannofranco.xml.Indent;

public class ExtensionSimple extends SchemaTag {

	private static final long serialVersionUID = -7362395531781461755L;
	private final java.util.List<ITag> attributes = new ArrayList<ITag>();
	private boolean built = false;
	private AnyAttribute anyAttribute;

	/**
	 * Creates a new instance of Extension Protetto
	 * 
	 * @param level
	 *            livello di indentazione
	 * @param container
	 *            tag contenitore
	 */
	protected ExtensionSimple(int level, ISchemaTag container) {
		super("extension", level, container);
	}
	
	/**
	 * @param baseType
	 * @return
	 */
	protected ExtensionSimple setBase(XmlType baseType) {
		addAttr("base", getLocalNS() + ":" + XmlType.getType(baseType));
		return this;
	}
	

	/**
	 * @param typeRef
	 * @return
	 */
	protected ExtensionSimple setBase(String typeName) {
		addAttr("base", feedFormPrefix(typeName));
		return this;
	}

	protected ExtensionSimple setBase(ContentType typeRef) {
		setBase(typeRef.getTypeName());
		return this;
	}
	
	/**
	 * add a "Attribute" tag with given name.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(String name) {
		Attribute att;
		add(att = new Attribute(getLevel() + 1, this));
		att.setName(name);
		return att;
	}

	/**
	 * add a "Attribute" tag with given name and use.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(String name, Use use) {
		Attribute ret = addTagAttribute(name);
		ret.setUse(use);
		return ret;
	}

	/**
	 * add a "Attribute" tag with given reference.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(Attribute ref) {
		Attribute att;
		add(att = new Attribute(getLevel() + 1, this));
		att.setRef(ref);
		return att;
	}

	/**
	 * add a "Attribute" tag with given reference and use.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(Attribute ref, Use use) {
		Attribute ret = addTagAttribute(ref);
		ret.setUse(use);
		return ret;
	}

	/**
	 * add a "Attribute" tag with given name and type.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(String name, XmlType type) {
		Attribute ret = addTagAttribute(name);
		ret.setType(type);
		return ret;
	}

	/**
	 * add a "Attribute" tag with given name, type and use.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(String name, XmlType type, Use use) {
		Attribute ret = addTagAttribute(name, type);
		ret.setUse(use);
		return ret;
	}

	/**
	 * add a "Attribute" tag with given name and type reference.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(String name, ContentType typeRef) {
		Attribute ret = addTagAttribute(name);
		ret.setType(typeRef);
		return ret;
	}

	/**
	 * add a "Attribute" tag with given name, type reference and use.
	 * 
	 * @return Attribute
	 */
	public Attribute addTagAttribute(String name, ContentType typeRef, Use use) {
		Attribute ret = addTagAttribute(name, typeRef);
		ret.setUse(use);
		return ret;
	}

	/**
	 * add a "Attribute" tag with given name and type name.
	 * 
	 * @return Attribute
	 */
//	public Attribute addAttributeTag(String name, String typeName) {
//		Attribute ret = addTagAttribute(name);
//		ret.setType(typeName);
//		return ret;
//	}

	/**
	 * add a "Attribute" tag with given name, type name and use.
	 * 
	 * @return Attribute
	 */
//	public Attribute addAttributeTag(String name, String typeName, Use use) {
//		Attribute ret = addAttributeTag(name, typeName);
//		ret.setUse(use);
//		return ret;
//	}
	
	/**
	 * Add an already declared first level AttributeGroup reference.
	 * 
	 * @return
	 */
	public AttributeGroup addAttributeGroup(AttributeGroup ref) {
		AttributeGroup att;
		attributes.add(att = new AttributeGroup(getLevel() + 1, this));
		att.setRef(ref);
		return att;
	}
	
	public AnyAttribute setAnyAttribute() {
		if (anyAttribute != null)
			throw new SchemaException("AnyAttribute gia' assegnato, "
					+ getClass().getName() + ":(" + getName() + ")");
		anyAttribute = new AnyAttribute(getLevel() + 1, this);
		return anyAttribute;
	}

	public AnyAttribute setAnyAttribute(String... namespace) {
		return setAnyAttribute().setNamespace(namespace);
	}

	public AnyAttribute setAnyAttribute(NsConstants namespace,
			Process processContent) {
		return setAnyAttribute().setNamespace(namespace.getVal())
				.processContents(processContent);
	}

	public AnyAttribute setAnyAttribute(Process processContent) {
		return setAnyAttribute().processContents(processContent);
	}
	/**
	 * aggiunge un tag Annotation che contiene un tag Documentation
	 * 
	 * @param explanation
	 *            Stringa di documentazione
	 * @return autoriferimento all'oggetto corrente
	 */
	public ExtensionSimple addDocumentation(String explanation) {
		addAnnotation().addDocumentation(explanation);
		return this;
	}

	/**
	 * aggiunge un tag Annotation che contiene un tag Documentation
	 * 
	 * @param explanation
	 *            testo della documentazione
	 * @param language
	 *            attributo di xml:lang, seguire le regole di i18n
	 * @return riferimento all'oggetto corrente
	 */
	public ExtensionSimple addDocumentation(String explanation, String language) {
		addAnnotation().addDocumentation(explanation, language);
		return this;
	}
	
	@Override
	public synchronized String toPrint() {
		if (!built) {
			build();
		}
		return super.toPrint();
	}

	@Override
	public synchronized String toSave() {
		if (!built) {
			build();
		}
		return super.toSave();
	}

	@Override
	public synchronized void setPrintIndent(Indent indent) {
		if (!built) {
			build();
		}
		super.setPrintIndent(indent);
	}

	@Override
	public synchronized void setSaveIndent(Indent indent) {
		if (!built) {
			build();
		}
		super.setSaveIndent(indent);
	}

	@Override
	protected synchronized final void setBlankLine(BlankLine blankLine) {
		if (!built) {
			build();
		}
		super.setBlankLine(blankLine);
	}

	private synchronized void build() {
		for (int i = 0; i < attributes.size(); i++) {
			add(attributes.get(i));
		}
		if (anyAttribute != null) {
			add(anyAttribute);
		}
		built = true;
	}
}
